#pragma once

#include <rocksdb/status.h>

#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <set>
#include <string>

#include "raft/options.h"
#include "raft/raft_log.h"

namespace libraft {
enum Role {
  kFollower = 0,
  kCandidate = 1,
  kLeader = 2,
};

class Meta;
class Raft;

class Context {
 public:
  Context(const Options& options);
  ~Context();

  void RecoverInit(const std::shared_ptr<Meta>& meta);
  void BecomeFollower(uint64_t new_term, const std::string leader_ip = "",
                      int port = 0);
  void BecomeCandidate();
  void BecomeLeader();

  const Options& options_;

  Role role;
  uint64_t current_term;
  std::string voted_for_ip;
  int voted_for_port;
  std::string leader_ip;
  int leader_port;
  uint32_t vote_quorum;
  uint64_t commit_index;
  std::atomic<uint64_t> last_applied;
  uint64_t last_applied_term;
  uint64_t last_op_time;

  std::set<std::string> members_;
  std::mutex global_mutex_;
  std::mutex apply_mutex_;
  std::condition_variable apply_cond_;

 private:
  Context(const Context&);
  void operator=(const Context&);
};

}  // namespace libraft